<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>元素自动避障演示（优化版）</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    colors: {
                        primary: '#3B82F6',
                        secondary: '#10B981',
                        accent: '#F59E0B',
                        danger: '#EF4444',
                        neutral: '#1F2937',
                        'neutral-light': '#F3F4F6'
                    },
                    fontFamily: {
                        sans: ['Inter', 'system-ui', 'sans-serif'],
                    },
                }
            }
        }
    </script>
    <style type="text/tailwindcss">
        @layer utilities {
            .content-auto {
                content-visibility: auto;
            }
            .floating-element {
                position: absolute;
                border-radius: 0.5rem;
                padding: 0.5rem;
                display: flex;
                align-items: center;
                justify-content: center;
                transition: all 0.1s ease; /* 减少动画时间 */
                cursor: move;
                user-select: none;
                box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.1);
            }
            .floating-element:hover {
                transform: translateY(-1px);
                box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
            }
            .floating-element.colliding {
                border: 1px solid #EF4444;
                background-color: rgba(239, 68, 68, 0.1);
            }
            .control-panel {
                position: fixed;
                bottom: 2rem;
                left: 50%;
                transform: translateX(-50%);
                background-color: white;
                padding: 1rem;
                border-radius: 0.75rem;
                box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
                z-index: 100;
            }
            .btn-action {
                @apply bg-primary text-white px-4 py-2 rounded-lg shadow-md hover:bg-primary/90 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-primary/50 active:scale-95;
            }
            .btn-toggle {
                @apply bg-secondary text-white px-4 py-2 rounded-lg shadow-md hover:bg-secondary/90 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-secondary/50 active:scale-95;
            }
            .btn-reset {
                @apply bg-neutral text-white px-4 py-2 rounded-lg shadow-md hover:bg-neutral/90 transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-neutral/50 active:scale-95;
            }
        }
    </style>
</head>
<body class="bg-gray-50 font-sans overflow-hidden">
    <header class="bg-white shadow-sm">
        <div class="container mx-auto px-4 py-4 flex justify-between items-center">
            <h1 class="text-xl font-bold text-neutral">元素自动避障演示（优化版）</h1>
            <div class="flex items-center space-x-2">
                <span id="collisionCount" class="px-3 py-1 bg-danger/20 text-danger rounded-full font-medium text-sm">
                    碰撞次数: 0
                </span>
                <span id="adjustmentCount" class="px-3 py-1 bg-secondary/20 text-secondary rounded-full font-medium text-sm">
                    调整次数: 0
                </span>
                <span id="fpsCounter" class="px-3 py-1 bg-neutral/20 text-neutral rounded-full font-medium text-sm">
                    FPS: 0
                </span>
            </div>
        </div>
    </header>

    <main id="viewport" class="relative w-full h-[calc(100vh-4rem)] bg-gray-100 overflow-hidden">
        <!-- 元素将通过JavaScript动态生成 -->
    </main>

    <div class="control-panel flex flex-wrap gap-2 justify-center">
        <button id="addElement" class="btn-action">
            <i class="fa fa-plus mr-2"></i>添加元素
        </button>
        <button id="add10Elements" class="btn-action">
            <i class="fa fa-plus-square mr-2"></i>添加10个
        </button>
        <button id="startAvoidance" class="btn-toggle">
            <i class="fa fa-play mr-2"></i>开始避障
        </button>
        <button id="reset" class="btn-reset">
            <i class="fa fa-refresh mr-2"></i>重置
        </button>
        <div class="flex items-center gap-2 px-2">
            <label for="avoidanceStrength" class="text-sm text-gray-600">避障强度:</label>
            <input type="range" id="avoidanceStrength" min="1" max="50" value="20" 
                class="w-32 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
            <span id="strengthValue" class="text-sm font-medium">20</span>
        </div>
        <div class="flex items-center gap-2 px-2">
            <label for="fpsLimit" class="text-sm text-gray-600">FPS限制:</label>
            <input type="range" id="fpsLimit" min="1" max="120" value="60" 
                class="w-32 h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
            <span id="fpsLimitValue" class="text-sm font-medium">60</span>
        </div>
    </div>

    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const viewport = document.getElementById('viewport');
            const addElementBtn = document.getElementById('addElement');
            const add10ElementsBtn = document.getElementById('add10Elements');
            const startAvoidanceBtn = document.getElementById('startAvoidance');
            const resetBtn = document.getElementById('reset');
            const avoidanceStrengthInput = document.getElementById('avoidanceStrength');
            const strengthValueDisplay = document.getElementById('strengthValue');
            const fpsLimitInput = document.getElementById('fpsLimit');
            const fpsLimitValueDisplay = document.getElementById('fpsLimitValue');
            const collisionCountDisplay = document.getElementById('collisionCount');
            const adjustmentCountDisplay = document.getElementById('adjustmentCount');
            const fpsCounterDisplay = document.getElementById('fpsCounter');
            
            // 优化后的元素数据结构 - 分离DOM和逻辑数据
            let elementData = [];
            let elementMap = new Map(); // ID -> DOM元素
            let isAvoiding = false;
            let avoidanceStrength = parseInt(avoidanceStrengthInput.value);
            let collisionCount = 0;
            let adjustmentCount = 0;
            let lastFrameTime = 0;
            let fpsLimit = parseInt(fpsLimitInput.value);
            let frameDuration = 1000 / fpsLimit; // 每帧持续时间(ms)
            let frameCount = 0;
            let lastFpsUpdateTime = 0;
            
            // 随机颜色数组
            const colors = [
                'bg-primary/80', 'bg-secondary/80', 'bg-accent/80', 
                'bg-purple-500/80', 'bg-pink-500/80', 'bg-indigo-500/80',
                'bg-teal-500/80', 'bg-orange-500/80', 'bg-emerald-500/80'
            ];
            
            // 网格分区 - 用于空间优化
            class Grid {
                constructor(cellSize) {
                    this.cellSize = cellSize;
                    this.cells = new Map(); // 存储格式: "x,y" -> [elementId, ...]
                }
                
                // 计算元素所在的网格坐标
                getCellCoords(element) {
                    const x1 = Math.floor(element.x / this.cellSize);
                    const y1 = Math.floor(element.y / this.cellSize);
                    const x2 = Math.floor((element.x + element.width) / this.cellSize);
                    const y2 = Math.floor((element.y + element.height) / this.cellSize);
                    
                    const cells = [];
                    for (let x = x1; x <= x2; x++) {
                        for (let y = y1; y <= y2; y++) {
                            cells.push(`${x},${y}`);
                        }
                    }
                    return cells;
                }
                
                // 将元素添加到网格
                addElement(element) {
                    const cells = this.getCellCoords(element);
                    element.cells = cells;
                    
                    cells.forEach(cellKey => {
                        if (!this.cells.has(cellKey)) {
                            this.cells.set(cellKey, []);
                        }
                        this.cells.get(cellKey).push(element.id);
                    });
                }
                
                // 从网格中移除元素
                removeElement(element) {
                    if (!element.cells) return;
                    
                    element.cells.forEach(cellKey => {
                        const cell = this.cells.get(cellKey);
                        if (cell) {
                            const index = cell.indexOf(element.id);
                            if (index !== -1) {
                                cell.splice(index, 1);
                            }
                            if (cell.length === 0) {
                                this.cells.delete(cellKey);
                            }
                        }
                    });
                    element.cells = [];
                }
                
                // 更新元素在网格中的位置
                updateElement(element) {
                    this.removeElement(element);
                    this.addElement(element);
                }
                
                // 获取可能与指定元素碰撞的所有元素ID
                getPotentialCollisions(element) {
                    const result = new Set();
                    const cells = this.getCellCoords(element);
                    
                    cells.forEach(cellKey => {
                        const cell = this.cells.get(cellKey);
                        if (cell) {
                            cell.forEach(id => {
                                if (id !== element.id) {
                                    result.add(id);
                                }
                            });
                        }
                    });
                    
                    return Array.from(result);
                }
                
                // 清空网格
                clear() {
                    this.cells.clear();
                }
            }
            
            // 创建网格分区，单元格大小设为100x100
            const grid = new Grid(100);
            
            // 生成随机位置和大小的元素
            function createRandomElement() {
                const viewportRect = viewport.getBoundingClientRect();
                const width = Math.floor(Math.random() * 80) + 80; // 80-160px
                const height = Math.floor(Math.random() * 40) + 40; // 40-80px
                const maxX = viewportRect.width - width;
                const maxY = viewportRect.height - height;
                
                // 创建DOM元素
                const element = document.createElement('div');
                element.className = `floating-element ${colors[Math.floor(Math.random() * colors.length)]}`;
                element.style.width = `${width}px`;
                element.style.height = `${height}px`;
                
                // 为元素添加唯一标识和内容
                const id = Date.now() + Math.floor(Math.random() * 1000);
                element.id = `element-${id}`;
                element.innerHTML = `
                    <div class="flex flex-col items-center">
                        <i class="fa fa-cube text-xl mb-1"></i>
                        <span class="font-medium">元素 ${elementData.length + 1}</span>
                    </div>
                `;
                
                // 创建数据对象
                const data = {
                    id,
                    x: Math.floor(Math.random() * maxX),
                    y: Math.floor(Math.random() * maxY),
                    width,
                    height,
                    domElement: element,
                    isDragging: false
                };
                
                // 添加到数据结构
                elementData.push(data);
                elementMap.set(id, element);
                grid.addElement(data);
                
                // 应用初始位置
                updateElementPosition(data);
                
                // 添加到DOM
                viewport.appendChild(element);
                
                // 添加拖拽功能
                makeDraggable(data);
                
                return data;
            }
            
            // 批量添加元素
            function addMultipleElements(count) {
                for (let i = 0; i < count; i++) {
                    createRandomElement();
                }
                checkCollisions();
            }
            
            // 使元素可拖拽
            function makeDraggable(data) {
                const element = data.domElement;
                let offsetX, offsetY;
                
                element.addEventListener('mousedown', function(e) {
                    data.isDragging = true;
                    const rect = element.getBoundingClientRect();
                    offsetX = e.clientX - rect.left;
                    offsetY = e.clientY - rect.top;
                    element.style.zIndex = 1000 + elementData.length; // 拖拽时置于顶层
                    element.classList.add('scale-105');
                });
                
                document.addEventListener('mousemove', function(e) {
                    if (!data.isDragging) return;
                    
                    const viewportRect = viewport.getBoundingClientRect();
                    
                    // 计算新位置，确保不超出视口
                    let newX = e.clientX - offsetX - viewportRect.left;
                    let newY = e.clientY - offsetY - viewportRect.top;
                    
                    // 边界检查
                    newX = Math.max(0, Math.min(newX, viewportRect.width - data.width));
                    newY = Math.max(0, Math.min(newY, viewportRect.height - data.height));
                    
                    // 更新数据
                    const oldX = data.x;
                    const oldY = data.y;
                    data.x = newX;
                    data.y = newY;
                    
                    // 仅当位置变化时更新网格和DOM
                    if (oldX !== newX || oldY !== newY) {
                        grid.updateElement(data);
                        updateElementPosition(data);
                    }
                    
                    // 拖拽时检查碰撞
                    if (isAvoiding) {
                        checkCollisions();
                    }
                });
                
                document.addEventListener('mouseup', function() {
                    if (data.isDragging) {
                        data.isDragging = false;
                        element.classList.remove('scale-105');
                    }
                });
            }
            
            // 更新元素的DOM位置
            function updateElementPosition(data) {
                data.domElement.style.transform = `translate(${data.x}px, ${data.y}px)`;
            }
            
            // 批量更新所有元素的位置（减少回流）
            function batchUpdateElementPositions() {
                const styleUpdates = [];
                
                // 收集所有样式更新
                elementData.forEach(data => {
                    styleUpdates.push(`${data.domElement.id}{transform:translate(${data.x}px, ${data.y}px)}`);
                });
                
                // 一次性应用所有样式更新
                if (styleUpdates.length > 0) {
                    const styleElement = document.createElement('style');
                    styleElement.textContent = styleUpdates.join('');
                    document.head.appendChild(styleElement);
                    
                    // 清理旧样式（下一帧后）
                    requestAnimationFrame(() => {
                        document.head.removeChild(styleElement);
                    });
                }
            }
            
            // 检测两个元素是否碰撞
            function checkCollision(element1, element2) {
                return (
                    element1.x < element2.x + element2.width &&
                    element1.x + element1.width > element2.x &&
                    element1.y < element2.y + element2.height &&
                    element1.y + element1.height > element2.y
                );
            }
            
            // 检查所有元素之间的碰撞
            function checkCollisions() {
                let hasCollision = false;
                let collisionPairs = [];
                
                // 重置所有元素的碰撞状态
                elementData.forEach(data => {
                    data.domElement.classList.remove('colliding');
                });
                
                // 清空网格并重新填充（因为元素可能已移动）
                grid.clear();
                elementData.forEach(data => {
                    grid.addElement(data);
                });
                
                // 使用网格优化碰撞检测
                for (let i = 0; i < elementData.length; i++) {
                    const element1 = elementData[i];
                    const potentialCollisions = grid.getPotentialCollisions(element1);
                    
                    for (let j = 0; j < potentialCollisions.length; j++) {
                        const element2Id = potentialCollisions[j];
                        const element2 = elementMap.get(element2Id);
                        
                        if (element2 && checkCollision(element1, element2)) {
                            // 记录碰撞对（避免重复处理）
                            const pairId = [element1.id, element2.id].sort().join('-');
                            if (!collisionPairs.includes(pairId)) {
                                collisionPairs.push(pairId);
                                
                                element1.domElement.classList.add('colliding');
                                element2.classList.add('colliding');
                                hasCollision = true;
                                collisionCount++;
                                updateCounters();
                                
                                // 如果启用了避障，执行避障逻辑
                                if (isAvoiding) {
                                    avoidCollision(element1, element2);
                                }
                            }
                        }
                    }
                }
                
                return hasCollision;
            }
            
            // 优化版避障算法：基于速度和加速度
            function avoidCollision(element1, element2) {
                // 计算两个元素中心之间的向量
                const center1 = {
                    x: element1.x + element1.width / 2,
                    y: element1.y + element1.height / 2
                };
                
                const center2 = {
                    x: element2.x + element2.width / 2,
                    y: element2.y + element2.height / 2
                };
                
                // 计算向量差
                let dx = center2.x - center1.x;
                let dy = center2.y - center1.y;
                
                // 计算距离（添加小值避免除零错误）
                const distance = Math.sqrt(dx * dx + dy * dy) + 0.001;
                
                // 计算重叠深度
                const overlapX = Math.min(element1.x + element1.width, element2.x + element2.width) - 
                                 Math.max(element1.x, element2.x);
                const overlapY = Math.min(element1.y + element1.height, element2.y + element2.height) - 
                                 Math.max(element1.y, element2.y);
                
                // 选择较小的重叠方向进行移动
                if (overlapX < overlapY) {
                    // 水平方向移动
                    const moveAmount = overlapX * avoidanceStrength * 0.05;
                    if (dx > 0) {
                        element1.x -= moveAmount;
                        element2.x += moveAmount;
                    } else {
                        element1.x += moveAmount;
                        element2.x -= moveAmount;
                    }
                } else {
                    // 垂直方向移动
                    const moveAmount = overlapY * avoidanceStrength * 0.05;
                    if (dy > 0) {
                        element1.y -= moveAmount;
                        element2.y += moveAmount;
                    } else {
                        element1.y += moveAmount;
                        element2.y -= moveAmount;
                    }
                }
                
                // 边界检查
                const viewportRect = viewport.getBoundingClientRect();
                
                if (element1.x < 0) element1.x = 0;
                if (element1.y < 0) element1.y = 0;
                if (element1.x + element1.width > viewportRect.width) {
                    element1.x = viewportRect.width - element1.width;
                }
                if (element1.y + element1.height > viewportRect.height) {
                    element1.y = viewportRect.height - element1.height;
                }
                
                if (element2.x < 0) element2.x = 0;
                if (element2.y < 0) element2.y = 0;
                if (element2.x + element2.width > viewportRect.width) {
                    element2.x = viewportRect.width - element2.width;
                }
                if (element2.y + element2.height > viewportRect.height) {
                    element2.y = viewportRect.height - element2.height;
                }
                
                // 更新网格位置
                grid.updateElement(element1);
                grid.updateElement(element2);
                
                adjustmentCount++;
                updateCounters();
            }
            
            // 更新计数器显示
            function updateCounters() {
                collisionCountDisplay.textContent = `碰撞次数: ${collisionCount}`;
                adjustmentCountDisplay.textContent = `调整次数: ${adjustmentCount}`;
            }
            
            // 更新FPS显示
            function updateFPS(currentTime) {
                frameCount++;
                
                // 每半秒更新一次FPS显示
                if (currentTime - lastFpsUpdateTime >= 500) {
                    const fps = Math.round((frameCount * 1000) / (currentTime - lastFpsUpdateTime));
                    fpsCounterDisplay.textContent = `FPS: ${fps}`;
                    
                    frameCount = 0;
                    lastFpsUpdateTime = currentTime;
                }
            }
            
            // 优化版避障循环
            function startAvoidanceLoop(timestamp) {
                if (!isAvoiding) return;
                
                // 限制FPS，避免不必要的计算
                if (timestamp - lastFrameTime < frameDuration) {
                    requestAnimationFrame(startAvoidanceLoop);
                    return;
                }
                
                lastFrameTime = timestamp;
                updateFPS(timestamp);
                
                // 检查并处理碰撞
                checkCollisions();
                
                // 批量更新所有元素位置
                // batchUpdateElementPositions(); // 注释掉以使用transform方法
                
                // 继续下一帧
                requestAnimationFrame(startAvoidanceLoop);
            }
            
            // 事件监听
            addElementBtn.addEventListener('click', createRandomElement);
            add10ElementsBtn.addEventListener('click', () => addMultipleElements(10));
            
            startAvoidanceBtn.addEventListener('click', function() {
                isAvoiding = !isAvoiding;
                
                if (isAvoiding) {
                    startAvoidanceBtn.innerHTML = '<i class="fa fa-pause mr-2"></i>暂停避障';
                    startAvoidanceBtn.classList.remove('bg-secondary');
                    startAvoidanceBtn.classList.add('bg-amber-500');
                    lastFrameTime = performance.now();
                    lastFpsUpdateTime = performance.now();
                    frameCount = 0;
                    startAvoidanceLoop(performance.now());
                } else {
                    startAvoidanceBtn.innerHTML = '<i class="fa fa-play mr-2"></i>开始避障';
                    startAvoidanceBtn.classList.remove('bg-amber-500');
                    startAvoidanceBtn.classList.add('bg-secondary');
                }
            });
            
            resetBtn.addEventListener('click', function() {
                // 移除所有元素
                elementData.forEach(data => {
                    data.domElement.remove();
                });
                
                // 重置数据结构
                elementData = [];
                elementMap.clear();
                grid.clear();
                
                // 重置状态
                isAvoiding = false;
                startAvoidanceBtn.innerHTML = '<i class="fa fa-play mr-2"></i>开始避障';
                startAvoidanceBtn.classList.remove('bg-amber-500');
                startAvoidanceBtn.classList.add('bg-secondary');
                
                // 重置计数器
                collisionCount = 0;
                adjustmentCount = 0;
                updateCounters();
                
                // 创建初始元素
                createRandomElement();
                createRandomElement();
                createRandomElement();
            });
            
            avoidanceStrengthInput.addEventListener('input', function() {
                avoidanceStrength = parseInt(this.value);
                strengthValueDisplay.textContent = avoidanceStrength;
            });
            
            fpsLimitInput.addEventListener('input', function() {
                fpsLimit = parseInt(this.value);
                frameDuration = 1000 / fpsLimit;
                fpsLimitValueDisplay.textContent = fpsLimit;
            });
            
            // 初始化：创建3个随机元素
            createRandomElement();
            createRandomElement();
            createRandomElement();
        });
    </script>
</body>
</html>
    